package de.uni.muenster.idw2.location.bluetooth.service;

import java.util.ArrayList;
import java.util.List;

public class FloorEstimator {
  List<BluetoothDeviceData> measurements = new ArrayList<BluetoothDeviceData>();
  List <FloorData> floorsData = new ArrayList<FloorData>();
  
  int prevEstimatedFloor;
  int lastEstimatedFloor;
  
class FloorData{
	  int floorId = Integer.MIN_VALUE;
	  int measurementCount = 0;
	  double dbMean;
	  double dbMax = Double.NEGATIVE_INFINITY;
	  public void addData(double db){
		  dbMean = (dbMean * measurementCount + db) / (measurementCount+1);  
		  if (db > dbMax){
			  dbMax=db;
		  }
		  measurementCount++;
	  } 
	  public FloorData (int floorId){
		  this.floorId = floorId;
	  } 
  }
  
  private FloorData getFloorById(int floorId){
	 for(FloorData data: floorsData){
		 if (data.floorId == floorId){
			 return data;
		 }
	 } 
	 return null;
  }
  
  private void updateFloorData(int floorId, double dbs){
	FloorData floor = getFloorById(floorId);
	if (floor ==null) {
		floor = new FloorData(floorId);
		floorsData.add(floor);
	}
	floor.addData(dbs);
  }
  
  //the estimation relies in the signal strengths of the  measurements considered
  private void estimate(){
	  boolean  firstTime = true;
	  int discoID = 0;
	  floorsData.clear();
	  //gathering the data doing in reverse so that the most recent values are first considered
	  for (int i = measurements.size()-1 ; i>=0; i--){
		  BluetoothDeviceData measurement = measurements.get(i);
		  //int currentDiscoID = measurement.getDiscoveryId();
		  if (firstTime){
		   discoID = measurement.getDiscoveryId();
		  }
		  int floor = DataIO.getFloorForHost(measurement.getId());
		  updateFloorData(floor, measurement.getDb_value());
	  }
	  
	  //checking the values.. and keeping the one having
	  analyzeData();
  }
  
  private void analyzeData() {
	double highestMean = Double.NEGATIVE_INFINITY;
	int floorHighestMean = Integer.MIN_VALUE;
	double highestMax = Double.NEGATIVE_INFINITY;
	int floorHighestMax = Integer.MIN_VALUE;
	//int countForMax = 0;
	//int countForMean = 0;
	
	for(FloorData data: floorsData){
		if (data.dbMean > highestMean){
			highestMean = data.dbMean; 
			floorHighestMean = data.floorId;
		}
		
		if (data.dbMax> highestMax){
			highestMax = data.dbMax; 
			floorHighestMax = data.floorId;
		}
	} 
	
	//pick and set up the floor with highest max
	//if the floor of the mean matches the one with the max.. just set it..
	//this is the happy case.
	if (floorHighestMax == floorHighestMean){
		updateLastEstimated(floorHighestMax);
	}
	else {
		//unhappy case..
		//prefer max to keep it simple..
		updateLastEstimated(floorHighestMax);		
	}
  }

  private void updateLastEstimated(int calcEstimatedFloor){
	  if (calcEstimatedFloor != lastEstimatedFloor){ 
	    prevEstimatedFloor = lastEstimatedFloor;
	    lastEstimatedFloor = calcEstimatedFloor;
	  }
   }
  
   /* Reset the data, eliminate the previous (old) data this could be done
   *  for example when the discovery cycle is finished.
   * */
  public void reset(){
	  floorsData.clear();
	  measurements.clear();
  }
  
  public void addMeasurement(BluetoothDeviceData measurement){
	  measurements.add(measurement);
	  estimate();
  }
  
  public int getEstimatedFloor(){
	 return lastEstimatedFloor; 	  
  }
  
  public int getPreviouslyEstimatedFloor(){
	return lastEstimatedFloor; 	  
  }
  
}
